﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using CoreEx.Common.Implementation.Linq;
using CoreEx.Database.Metadata;
using CoreEx.Database.Query;
using LinFu.IoC.Interfaces;
using LinFu.IoC;
using LinFu.Reflection;

namespace CoreEx.Database.Linq
{
    public abstract class ExpressionTranslatorBase_old : IExpressionTranslator_old, IInitialize<IServiceRequestResult>
    {
        private IEnumerable<IExpressionRewriter> _rewriters;
        
        
        protected static Expression StripQuotes(Expression e)
        {
            while (e.NodeType == ExpressionType.Quote || e.NodeType == ExpressionType.Convert)
            {
                e = ((UnaryExpression)e).Operand;
            }
            return e;
        }
        
        protected readonly string _connectionName;
        
        public IExpressionFactory ExpressionFactory { get; set; }

        public ITypeMappingRepository MappingRepository { get; set; }

        public IMetadataRepository MetadataRepository { get; set; }

        public IQueryableResolver QueryableResolver { get; set; }


        protected IServiceContainer ServiceContainer { get; set; }

        

        protected ITranslateResult_old _translateResult;


        protected ExpressionTranslatorBase_old(string connectionName)
        {
            _connectionName = connectionName;            
        }


        public ITranslateResult_old Translate(Expression expression)
        {            
            //if (typeof(IQueryable).IsAssignableFrom(expression.Type))
            //    expression = Evaluator.PartialEval(expression);

            foreach (var rewriter in _rewriters)
            {
                expression = rewriter.Rewrite(expression);
            }


            return TranslateExpression(expression);

        //    return Visit(expression);            
        }

        public ITranslateResult_old TranslateAsDerivedTable(Expression expression)
        {
            foreach (var rewriter in _rewriters)
            {
                expression = rewriter.Rewrite(expression);
            }


            return TranslateExpressionAsDerivedTable(expression);
        }


        public ITranslateResult_old Translate(Expression expression, ITranslateResult_old parent, bool createSubQuery)
        {
            foreach (var rewriter in _rewriters)
            {
                expression = rewriter.Rewrite(expression);
            }


            return TranslateExpression(expression,parent, createSubQuery);
        }

        public ITranslateResult_old Translate(IRelationLoadTarget relationLoadTarget, IProjectionTarget_old outerProjectionTarget, ITranslateResult_old outerTranslateResult)
        {
            foreach (var rewriter in _rewriters)
            {
                relationLoadTarget.LoadExpression = (LambdaExpression)rewriter.Rewrite(relationLoadTarget.LoadExpression);
            }


            return TranslateExpression(relationLoadTarget, outerProjectionTarget, outerTranslateResult);
        }

        public ITranslateResult_old Translate(Expression expression, PropertyInfo property, IProjectionTarget_old outerProjectionTarget)
        {
            foreach (var rewriter in _rewriters)
            {
                expression = rewriter.Rewrite(expression);
            }
            return TranslateExpression(expression, property, outerProjectionTarget);

        }

        protected abstract ITranslateResult_old TranslateExpression(Expression expression, PropertyInfo property,
                                                                IProjectionTarget_old outerProjectionTarget);

        protected abstract ITranslateResult_old TranslateExpression(Expression expression);
        protected abstract ITranslateResult_old TranslateExpressionAsDerivedTable(Expression expression);

        protected abstract ITranslateResult_old TranslateExpression(Expression expression, ITranslateResult_old parent, bool createSubQuery);

        protected abstract ITranslateResult_old TranslateExpression(IRelationLoadTarget relationLoadTarget, IProjectionTarget_old outerProjectionTarget, ITranslateResult_old outerTranslateResult);
        





        protected IExpression Visit(Expression expression)
        {
            switch (expression.NodeType)
            {               
                case ExpressionType.Negate:
                case ExpressionType.NegateChecked:
                case ExpressionType.Not:
                case ExpressionType.Convert:
                case ExpressionType.ConvertChecked:
                case ExpressionType.ArrayLength:
                case ExpressionType.Quote:
                case ExpressionType.TypeAs:
                    return VisitUnary((UnaryExpression)expression);
                case ExpressionType.Lambda:
                    return VisitLambda((LambdaExpression)expression);
                case ExpressionType.Add:
                case ExpressionType.AddChecked:
                case ExpressionType.Subtract:
                case ExpressionType.SubtractChecked:
                case ExpressionType.Multiply:
                case ExpressionType.MultiplyChecked:
                case ExpressionType.Divide:
                case ExpressionType.Modulo:
                case ExpressionType.And:
                case ExpressionType.AndAlso:
                case ExpressionType.Or:
                case ExpressionType.OrElse:
                case ExpressionType.LessThan:
                case ExpressionType.LessThanOrEqual:
                case ExpressionType.GreaterThan:
                case ExpressionType.GreaterThanOrEqual:
                case ExpressionType.Equal:
                case ExpressionType.NotEqual:
                case ExpressionType.Coalesce:
                case ExpressionType.ArrayIndex:
                case ExpressionType.RightShift:
                case ExpressionType.LeftShift:
                case ExpressionType.ExclusiveOr:
                    return this.VisitBinary((BinaryExpression)expression);
                case ExpressionType.MemberAccess:
                    return this.VisitMemberAccess((MemberExpression)expression);
                case ExpressionType.Constant:
                    return this.VisitConstant((ConstantExpression)expression);
                case ExpressionType.Call:
                    return this.VisitMethodCall((MethodCallExpression)expression);
                case ExpressionType.Parameter:
                    return this.VisitParameter((ParameterExpression)expression);
                case ExpressionType.New:
                    return this.VisitNew((NewExpression)expression);
                case ExpressionType.Conditional:
                    return VisitConditional((ConditionalExpression) expression);
                case ExpressionType.MemberInit:
                    return VisitMemberInit((MemberInitExpression) expression);
                

            }

            throw new NotSupportedException(expression.ToString());
        }

        protected virtual IExpression VisitLambda(LambdaExpression expression)
        {
            return Visit(expression.Body);
        }

        protected abstract IExpression VisitMemberInit(MemberInitExpression memberInitExpression);

        protected abstract IExpression VisitConditional(ConditionalExpression conditionalExpression);

        protected abstract IExpression VisitUnary(UnaryExpression expression);

        protected abstract IExpression VisitMethodCall(MethodCallExpression expression);

        protected abstract IExpression VisitConstant(ConstantExpression expression);

        protected abstract IExpression VisitMemberAccess(MemberExpression expression);

        protected abstract IExpression VisitBinary(BinaryExpression expression);

        protected abstract IExpression VisitParameter(ParameterExpression expression);

        protected abstract IExpression VisitNew(NewExpression expression);

        public virtual void Initialize(IServiceRequestResult source)
        {
            ServiceContainer = source.Container;
            ExpressionFactory = source.Container.GetService<IExpressionFactory>();
            MappingRepository = source.Container.GetService<ITypeMappingRepository>(_connectionName);
            MetadataRepository = source.Container.GetService<IMetadataRepository>(_connectionName);
            QueryableResolver = source.Container.GetService<IQueryableResolver>();
            _rewriters = source.Container.GetServices<IExpressionRewriter>();
        }
    }
}
